﻿using System;
using System.Collections.Generic;
using System.Timers;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using FlanProject.Combats.Actions;
using FlanProjectData.Characters;
using FlanProjectData.Managers;
using FlanProjectData.Skills;
using FlanProjectData.Sprites;

namespace FlanProject.Combats
{
    public class CombatPlayer : CombatCharacter
    {
        #region Properties

        #region Player Data

        public Image Image { get; set; }

        #endregion

        #region Combat Data

        private static MoveList moveList;

        public bool IsCastTimeOver { get; set; }
        public Skill PlayerMove { get; set; }

        #endregion

        #endregion

        // Player constructor
        public CombatPlayer(Player player) : base()
        {
            if (player == null)
                throw new ArgumentNullException("Unknown player.");

            Name = player.Name;
            Image = player.Portrait;
            Stats = player.BaseStats;
            IdleAnimation = player.IdleAnimation;
            WalkAnimation = player.WalkAnimation;
            RunAnimation = player.RunAnimation;
            AttackAnimation = player.AttackAnimation;
            DeathAnimation = player.DeathAnimation;
            VictoryAnimation = player.VictoryAnimation;
            SpellcastAnimation = player.SpellcastAnimation;
            EndSpellcastAnimation = player.EndSpellcastAnimation;
            DefenseAnimation = player.DefenseAnimation;
            OnHitAnimation = player.OnHitAnimation;
            OnStartLookDirection = player.OnStartLookDirection;
            IsFloating = player.IsFloating;
            HeightOffset = player.HeightOffset;
            Skills = player.Skills;
        }

        // Load character animations, skills and move list
        public void LoadContent(ContentManager contentManager)
        {
            // Load character image
            Image.LoadContent(contentManager);

            // Load character animations and set default animation
            IdleAnimation.LoadContent(contentManager);
            WalkAnimation.LoadContent(contentManager);
            RunAnimation.LoadContent(contentManager);
            AttackAnimation.LoadContent(contentManager);
            DeathAnimation.LoadContent(contentManager);
            VictoryAnimation.LoadContent(contentManager);
            SpellcastAnimation.LoadContent(contentManager);
            EndSpellcastAnimation.LoadContent(contentManager);
            DefenseAnimation.LoadContent(contentManager);
            OnHitAnimation.LoadContent(contentManager);
            CurrentAnimation.PlayAnimation(IdleAnimation);

            // Load character skills
            foreach (var skill in Skills)
            {
                var newSkill = contentManager.Load<Skill>(@"Data\Skills\" + skill);
                newSkill.LoadContent(contentManager);
                SkillList.Add(newSkill);
            }

            moveList = new MoveList(SkillList);

            // Load AI
            if (PlayerIndex != 0)
                ArtificialIntelligence = new ArtificialIntelligence(this);

            base.LoadContent();
        }

        // Cooldown after casting a spell
        public void Cooldown_Elapsed(object sender, ElapsedEventArgs e)
        {
            CurrentState = States.Idle;
        }

        // Endspellcasting state after casting a spell
        public void Spellcasting_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (PlayerMove != null)
            {
                CurrentState = States.EndSpellcasting;

                // Wait for the spell to cool down
                var cooldown = new Timer() { Interval = PlayerMove.Stats.Cooldown, AutoReset = false };
                cooldown.Elapsed += new ElapsedEventHandler(Cooldown_Elapsed);
                cooldown.Start();

                IsCastTimeOver = true;
            }
        }

        // Handle user states
        public override void HandleUserStates()
        {
            // Cancel spellcasting if player is hit
            if (PlayerMove != null && CurrentState == States.Hit)
                PlayerMove = null;

            // Cast spell if a move is detected and the player is not already spellcasting
            if (PlayerMove != null && PlayerMove.Type == Skill.Types.Spell
                && CurrentState != States.Spellcasting && CurrentState != States.EndSpellcasting)
            {
                CurrentState = States.Spellcasting;

                // Cast time
                var spellcasting = new Timer() { Interval = PlayerMove.Stats.CastTime, AutoReset = false };
                spellcasting.Elapsed += new ElapsedEventHandler(Spellcasting_Elapsed);
                spellcasting.Start();
            }

            base.HandleUserStates();
        }

        // Define user input
        public void HandleInput(GameTime gameTime)
        {
            Direction = Vector2.Zero;

            if (InputManager.CurrentKeyboardState.GetPressedKeys().Length == 0 && CurrentState == States.Defending)
                CurrentState = States.Idle;

            if (CurrentState == States.Dead || CurrentState == States.Hit || CurrentState == States.Spellcasting
                    || CurrentState == States.Defending || CurrentState == States.Victory)
                return;

            if (InputManager.IsActionPressed(InputManager.Action.Attack))
                CurrentState = States.Attacking;

            if (InputManager.IsActionPressed(InputManager.Action.Defend))
                CurrentState = States.Defending;

            if (InputManager.IsActionPressed(InputManager.Action.MoveLeft))
            {
                CurrentState = States.Walking;
                Direction = new Vector2(MOVE_LEFT, 0);
            }

            if (InputManager.IsActionPressed(InputManager.Action.MoveRight))
            {
                CurrentState = States.Walking;
                Direction = new Vector2(MOVE_RIGHT, 0);
            }

            if (InputManager.CurrentKeyboardState.GetPressedKeys().Length == 0
                && (CurrentState == States.Walking || CurrentState == States.Running))
                CurrentState = States.Idle;

            if (CurrentState != States.Idle && CurrentState != States.Defending && CurrentState != States.Walking
                && CurrentState != States.Running)
            {
                if (CurrentAnimation.IsAnimationComplete)
                    CurrentState = States.Idle;
            }
        }

        // Update character skills
        public void UpdateSequence(GameTime gameTime)
        {
            // Get the updated input manager
            SequenceManager.Update(gameTime);

            // Detect and record the current player's most recent move
            var newMove = moveList.DetectMove();

            // Set it as the new player move
            if (newMove != null)
                PlayerMove = newMove;
        }

        // Update character
        public override void Update(GameTime gameTime)
        {
            if (PlayerIndex == 0)
            {
                UpdateSequence(gameTime);
                HandleInput(gameTime);

                PreviousPosition = Position;
                Position += Direction * Stats.MovementSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
            }
            else
            {
                // Choose action if none selected
                if (Action == null)
                {
                    Action = ArtificialIntelligence.ChooseAction();
                }
                else
                {
                    // If action returned, execute it
                    if (Action.Stage == CombatAction.CombatActionStage.NotStarted)
                        Action.Start();
                }
            }

            HandleUserStates();

            base.Update(gameTime);
        }

        // Draw current animation
        public override void Draw(SpriteBatch spriteBatch, GameTime gameTime)
        {
            var width = FlanProject.Instance.GraphicsDevice.Viewport.Width;

            // Flip the sprite to face the way we are moving
            if (Direction.X > 0)
                Flip = OnStartLookDirection == MOVE_LEFT ? SpriteEffects.FlipHorizontally : SpriteEffects.None;
            else if (Direction.X < 0)
                Flip = OnStartLookDirection == MOVE_LEFT ? SpriteEffects.None : SpriteEffects.FlipHorizontally;

            CurrentAnimation.Draw(spriteBatch, Flip, Position, gameTime);
            Image.Draw(spriteBatch, new Vector2((float)(width * 0.01 + width * 0.2 * PlayerIndex),
                (float)(spriteBatch.GraphicsDevice.Viewport.Height * 0.03)));
        }
    }
}
